/** @jsxImportSource @emotion/react */
import { ChangeEvent, createElement, HTMLAttributes, useEffect, useState, type FC } from 'react';
import classNames from 'classnames';
import { CSSObject } from '@emotion/react';
import { useClickAway, useDebounce, useEnsuredForwardedRef, useUpdateEffect } from 'react-use';
import { searchDefaultProps } from './defaultProps';
import type { SearchOptions } from './type';

export interface SearchProps extends SearchOptions, Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'results'> {}

const Search: FC<SearchProps> = (props) => {
  const {
    className,
    ui,
    icon,
    category,
    placeholder,
    results,
    loading,
    disabled,
    duration,
    noResultsMessage,
    noResultsDescription,
    forwardedRef,
    onChange,
    ...searchProps
  } = props;
  const [focus, setFocus] = useState(false);
  const [active, setActive] = useState(false);
  const [empty, setEmpty] = useState(true);
  const [css, setCss] = useState<string>();
  const [styles, setStyles] = useState<CSSObject>();
  const [val, setVal] = useState<string>('');
  const [debouncedValue, setDebouncedValue] = useState<string>('');

  const ensuredForwardRef = useEnsuredForwardedRef(forwardedRef!);

  useDebounce(
    () => {
      setDebouncedValue(val);
    },
    300,
    [val],
  );

  useUpdateEffect(() => {
    onChange?.(debouncedValue);
  }, [debouncedValue]);

  useClickAway(ensuredForwardRef, () => {
    setFocus(false);
  });

  useEffect(() => {
    if (results === undefined) {
      if (focus && active) {
        setCss('transition visible animating scale out');
        setStyles({ display: 'block !important', animationDuration: `${duration}ms` });
        setTimeout(() => {
          setCss('transition hidden');
          setStyles(undefined);
          setActive(false);
          setEmpty(true);
        }, duration || 0);
      }
    } else {
      setEmpty(results.length === 0 ? true : false);
      if (focus) {
        if (!active) {
          setActive(true);
          setCss('animating transition scale in');
          setStyles({ display: 'block !important', animationDuration: `${duration}ms` });
          setTimeout(() => {
            setCss('transition visible');
            setStyles({ display: 'block !important' });
          }, duration || 0);
        }
      } else {
        setCss('transition visible animating scale out');
        setStyles({ display: 'block !important', animationDuration: `${duration}ms` });
        setTimeout(() => {
          setCss('transition hidden');
          setStyles(undefined);
          setActive(false);
        }, duration || 0);
      }
    }
  }, [focus, results]);

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setVal(e.currentTarget.value);
  };

  const inputEl = (
    <input
      key="input"
      className="prompt"
      type="text"
      value={val}
      placeholder={placeholder}
      onChange={handleChange}
      onFocus={() => setFocus(true)}
    />
  );

  return createElement(
    'div',
    {
      ref: ensuredForwardRef,
      className: classNames({ ui, category, disabled }, loading, { loading }, 'search', { focus }, className),
      ...searchProps,
    },
    [
      icon ? (
        <div key="input" className="ui icon input">
          {inputEl}
          <i className={`${icon} icon`} />
        </div>
      ) : (
        inputEl
      ),
      <div key="results" className={classNames('results', css)} css={styles}>
        {active &&
          (empty ? (
            <div className="message empty">
              <div className="header">{noResultsMessage}</div>
              <div className="description">{noResultsDescription}</div>
            </div>
          ) : (
            results?.map((value, index) => {
              if (typeof value === 'string') {
                return (
                  <div className="result" key={index}>
                    <div className="content">{value}</div>
                  </div>
                );
              } else {
                if (value.category) {
                  return (
                    <div className="category" key={index}>
                      <div className="name">{value.category}</div>
                      <div className="results">
                        <div className="result">
                          <div className="content">TODO...</div>
                        </div>
                      </div>
                    </div>
                  );
                } else {
                  return (
                    <div className="result" key={index}>
                      {value.image && (
                        <div className="image">
                          <img src={value.image} />
                        </div>
                      )}
                      <div className="content">
                        {value.price && <div className="price">{value.price}</div>}
                        {value.title && <div className="title">{value.title}</div>}
                        {value.description && <div className="description">{value.description}</div>}
                      </div>
                    </div>
                  );
                }
              }
            })
          ))}
      </div>,
    ],
  );
};

Search.displayName = 'Search';
Search.defaultProps = searchDefaultProps;

export default Search;
